package com.cardone.api.support;

import com.cardone.common.cache.util.CacheUtils;
import com.cardone.common.util.MapperUtils;
import com.cardone.common.util.ReturnDataUtils;
import com.cardone.common.util.StringUtils;
import com.cardone.context.CodeException;
import com.cardone.context.ContextHolder;
import com.cardone.security.token.util.TokenUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2015/3/2.
 */
@lombok.extern.slf4j.Slf4j
public class ExecutionPackageServiceDefaultSupport implements ExecutionPackageServiceSupport {
    /**
     * 数据位置
     */
    @Setter
    @Getter
    private static Resource configLocation;

    private static String configLocationPath;

    public static PackageServiceSupport loadPackageServiceSupport(String beanId) {
        if (configLocation == null) {
            throw new CodeException("1000404");
        }

        try {
            if (StringUtils.isBlank(configLocationPath)) {
                configLocationPath = configLocation.getFile().getAbsolutePath();
            }

            String packageServiceSupportFilename = configLocationPath + File.separator + beanId;

            File packageServiceSupportFile = FileUtils.getFile(packageServiceSupportFilename);

            if ((packageServiceSupportFile == null) || !packageServiceSupportFile.exists()) {
                if (log.isDebugEnabled()) {
                    log.error(beanId + ":没有对应的本地api配置," + packageServiceSupportFile.getAbsolutePath());
                } else {
                    return null;
                }
            }

            final String packageServiceSupportString = FileUtils.readFileToString(packageServiceSupportFile, Charsets.UTF_8);

            if (StringUtils.isBlank(packageServiceSupportString)) {
                return null;
            }

            return MapperUtils.fromJson(PackageServiceSupport.class, packageServiceSupportString);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    @Override
    public Object execution(String beanId, Map<String, Object> parameterMap) {
        PackageServiceSupport packageServiceSupport;

        if (log.isDebugEnabled()) {
            packageServiceSupport = loadPackageServiceSupport(beanId);
        } else {
            packageServiceSupport = CacheUtils.get(GetWebPackageServiceSupport.class.getName(), beanId, key -> loadPackageServiceSupport(beanId));
        }

        if (packageServiceSupport == null) {
            return null;
        }

        return invokeMethod(packageServiceSupport, parameterMap);
    }

    private Object invokeMethod(PackageServiceSupport packageServiceSupport, Map<String, Object> parameterMap) {
        if (StringUtils.isBlank(packageServiceSupport.getBeanId())) {
            throw new CodeException("未定义getBeanId");
        }

        Object beanObject = ContextHolder.getBean(PackageServiceFunction.class, packageServiceSupport.getBeanId());

        if (beanObject == null) {
            throw new CodeException("spring bean未定义，beanId:" + packageServiceSupport.getBeanId()).setCode("spring bean not");
        }

        Map<String, Object> newParameterMap = newParameterMap(packageServiceSupport, parameterMap);

        Object data;

        if (beanObject instanceof PackageServiceFunction) {
            PackageServiceFunction packageServiceFunction = (PackageServiceFunction) beanObject;

            data = packageServiceFunction.execution(newParameterMap);
        } else {
            try {
                data = MethodUtils.invokeMethod(beanObject, packageServiceSupport.getMethodName(), newParameterMap);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                }

                if (e.getCause() instanceof RuntimeException) {
                    throw (RuntimeException) e.getCause();
                }

                if (e.getCause() == null) {
                    throw new RuntimeException(e);
                }

                throw new RuntimeException(e.getCause());
            }
        }

        if (StringUtils.isNotBlank(packageServiceSupport.getMapId())) {
            data = toMapIdData(data, packageServiceSupport.getMapId());
        }

        if (MapUtils.isEmpty(packageServiceSupport.getTokenGenerateUrlMap())) {
            if (StringUtils.isBlank(packageServiceSupport.getReturnDataName())) {
                if (data == null) {
                    return MapperUtils.toJson(ReturnDataUtils.newMap(data));
                }

                return data;
            }

            return MapperUtils.toJson(ReturnDataUtils.newMap(packageServiceSupport.getReturnDataName(), data));
        }

        Map<String, Object> dataMap;

        if (data instanceof Map) {
            dataMap = (Map<String, Object>) data;
        } else {
            dataMap = ReturnDataUtils.newMap(packageServiceSupport.getReturnDataName(), data);
        }

        putToken(parameterMap, packageServiceSupport, dataMap);

        return dataMap;
    }

    private Map<String, Object> newParameterMap(PackageServiceSupport packageServiceSupport, Map<String, Object> parameterMap) {
        if (MapUtils.isEmpty(packageServiceSupport.getDefaultParameterMap()) && MapUtils.isEmpty(packageServiceSupport.getOverrideParameterMap())) {
            return parameterMap;
        }

        Map<String, Object> newParameterMap = Maps.newHashMap();

        if (MapUtils.isNotEmpty(packageServiceSupport.getDefaultParameterMap())) {
            newParameterMap.putAll(packageServiceSupport.getDefaultParameterMap());
        }

        newParameterMap.putAll(parameterMap);

        if (MapUtils.isNotEmpty(packageServiceSupport.getOverrideParameterMap())) {
            newParameterMap.putAll(packageServiceSupport.getOverrideParameterMap());
        }
        return newParameterMap;
    }

    private Object toMapIdData(Object data, String mapId) {
        if (data == null) {
            return null;
        }

        if (data instanceof String) {
            return data;
        }

        if (org.apache.commons.lang3.StringUtils.isBlank(mapId)) {
            return data;
        }

        if (org.dozer.util.CollectionUtils.isCollection(data.getClass())) {
            List sourceFieldList = (List) data;

            if (org.apache.commons.collections.CollectionUtils.isEmpty(sourceFieldList)) {
                return Lists.newArrayList();
            }

            List destinationList = Lists.newArrayList();

            for (Object sourceField : sourceFieldList) {
                destinationList.add(ContextHolder.getBean(org.dozer.Mapper.class).map(sourceField, Map.class, mapId));
            }

            return destinationList;
        }

        return ContextHolder.getBean(org.dozer.Mapper.class).map(data, java.util.Map.class, mapId);
    }

    private void putToken(Map<String, Object> parameterMap, PackageServiceSupport packageServiceSupport, Map<String, Object> returnMap) {
        if (MapUtils.isEmpty(packageServiceSupport.getTokenGenerateUrlMap())) {
            return;
        }

        String clientId = MapUtils.getString(parameterMap, TokenUtils.getClientIdParamName());
        String tokenKey = MapUtils.getString(parameterMap, TokenUtils.getTokenParamName());

        for (Map.Entry<String, String> tokenGenerateUrlEntry : packageServiceSupport.getTokenGenerateUrlMap().entrySet()) {
            String returnValue = TokenUtils.generateForUrls(clientId, tokenKey, tokenGenerateUrlEntry.getValue()).getKey();

            returnMap.put(tokenGenerateUrlEntry.getKey(), returnValue);
        }
    }
}